# 合并重复的导入
from PyQt6.QtCore import QThread, pyqtSignal,QWaitCondition,QMutex
import time
import sqlite3
import random
from datetime import datetime, timedelta
import hashlib
from dataclasses import dataclass
import sys

from utils.click_utils import ClickUtils
from utils.util import humanized_click,humanized_click2
from utils.log_service import LogService
from utils.wrap_utils import handle_exception
from utils import sh_jl as sjudb
from utils import sh_utils as shDb
from utils import gt_utils as gtDb
from utils import fl_cur_log_utils as fcluDb
from utils import resume_utils as ruDb
import os
from utils.playwright_utils import PlaywrightUtils
import re
from common_utils import get_session_id,check_resume_completeness, file_logger, ResourceMonitor,safe_inner_text,safe_get_attribute,scroll_view_and_click,check_jj,check_hd_page,check_gt_page

import json
from playwright.sync_api import sync_playwright, TimeoutError as PlaywrightTimeoutError
from utils.config_manager import config


logger = LogService()
# 创建文件日志实例


def extract_resume_data2(name, wechat, pi):
    current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    # Initialize the dictionary with default empty values
    resume_data = {
        "createTime": current_time,
        "personal_info": {
            "name": name,
            "age": pi.get("age",""),
            "wechat": wechat,
            "work_experience": "",
            "education_level": "",
            "current_status": "",
            "location": "",
            "phone": "",
            "gender": ""
        },
        "job_expectations": [],
        "work_experiences": [],
        "project_experiences": [],
        "education": [],
        "personal_advantages": "",
        "training_experiences": [],
        "personal_works": []
    }
    return resume_data


def extract_resume_data(page):
    current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    # Initialize the dictionary with default empty values
    resume_data = {
        "createTime": current_time,
        "personal_info": {
            "name": "",
            "age": "",
            "work_experience": "",
            "education_level": "",
            "current_status": "",
            "location": "",
            "phone": "",
            "gender": ""
        },
        "job_expectations": [],
        "work_experiences": [],
        "project_experiences": [],
        "education": [],
        "personal_advantages": "",
        "training_experiences": [],
        "personal_works": []
    }

    # Helper function to wait for an element with timeout
    def wait_for_selector(selector, timeout=5000):
        try:
            page.wait_for_selector(selector, timeout=timeout)
            return page.query_selector(selector)
        except PlaywrightTimeoutError:
            return None

    # Extract Personal Information
    try:
        resume_data["personal_info"]["name"] = safe_inner_text(
            wait_for_selector("//span[@class='resume-basic-new__name']"))

        gender_img = wait_for_selector("//div[@class='km-image km-image--loaded resume-basic-new__gender']//img")
        if gender_img:
            gsc = safe_get_attribute(gender_img, "src")
            if gsc and 'female' in gsc:
                resume_data["personal_info"]["gender"] = "女"
            elif gsc and 'male' in gsc:
                resume_data["personal_info"]["gender"] = "男"
            else:
                resume_data["personal_info"]["gender"] = "未知"
        else:
            resume_data["personal_info"]["gender"] = "未知"

        age_element = wait_for_selector("//span[contains(@class, 'resume-basic-new__meta-item') and contains(., '岁')]")
        age_text = safe_inner_text(age_element)
        resume_data["personal_info"]["age"] = age_text.split('(')[
            0].strip() if age_text and '(' in age_text else age_text

        work_exp_element = wait_for_selector(
            "//span[contains(@class, 'resume-basic-new__meta-item') and contains(., '工作')]")
        work_exp_text = safe_inner_text(work_exp_element)
        resume_data["personal_info"]["work_experience"] = work_exp_text.replace("工作",
                                                                                "").strip() if work_exp_text else ""

        education_element = wait_for_selector("//span[@class='resume-basic-new__meta-item'][.//img[contains(@src, 'meta-edu')]]")
        if education_element:
            education_text = safe_inner_text(education_element)
            # 去掉可能的空白字符，只保留学历文本
            resume_data["personal_info"]["education_level"] = education_text.strip() if education_text else ""
        resume_data["personal_info"]["current_status"] = safe_inner_text(
            wait_for_selector("//span[contains(@class, 'resume-basic-new__meta-item') and contains(., '离职')]"))

        location_element = wait_for_selector(
            "//span[contains(@class, 'resume-basic-new__meta-item') and contains(., '现居')]")
        location_text = safe_inner_text(location_element)
        resume_data["personal_info"]["location"] = location_text.replace("现居", "").strip() if location_text else ""

        phone_element = wait_for_selector("//div[@class='resume-basic-new__contacts--phone']//div[contains(@class, 'is-ml-20')]/div[not(@class)]")
        phone_text = safe_inner_text(phone_element)
        resume_data["personal_info"]["phone"] = phone_text.split('(')[
            0].strip() if phone_text and '(' in phone_text else phone_text
    except Exception as e:
        print(f"Error extracting personal info: {e}")

    # Extract Job Expectations
    try:
        job_expectations = page.query_selector_all("//div[@class='new-resume-purposes__item']") or []
        for expectation in job_expectations:
            if not expectation.is_visible():
                continue
            job_data = {
                "city": safe_inner_text(expectation.query_selector("//span[@class='new-resume-purposes__item-city']")),
                "position": safe_inner_text(
                    expectation.query_selector("//span[@class='new-resume-purposes__item-type']")),
                "salary": safe_inner_text(
                    expectation.query_selector("//div[@class='new-resume-purposes__item-salary']"))
            }
            # Only append if at least one field is non-empty
            if any(job_data.values()):
                resume_data["job_expectations"].append(job_data)
    except Exception as e:
        print(f"Error extracting job expectations: {e}")

    # Extract Work Experiences
    try:
        work_experiences = page.query_selector_all("//div[@class='new-work-experiences__item']") or []
        for work in work_experiences:
            if not work.is_visible():
                continue
            work_data = {
                "company": safe_inner_text(
                    work.query_selector("//span[@class='new-work-experiences__work-name']//span")),
                "position": safe_inner_text(
                    work.query_selector("//span[@class='new-work-experiences__work-job-title']")),
                "duration": safe_inner_text(work.query_selector("//span[@class='new-work-experiences__work-time']")),
                "salary": safe_inner_text(work.query_selector("//span[@class='salary-dot']")),
                "skills": [safe_inner_text(tag) for tag in
                           work.query_selector_all("//span[@class='new-resume-detail-skill-tag']//span") or []],
                "description": safe_inner_text(work.query_selector("//div[@class='new-work-experiences__desc']//span"))
            }
            if any(work_data.values()):
                resume_data["work_experiences"].append(work_data)
    except Exception as e:
        print(f"Error extracting work experiences: {e}")

    # Extract Project Experiences
    try:
        project_experiences = page.query_selector_all("//div[@class='new-project-experiences__item']") or []
        for project in project_experiences:
            if not project.is_visible():
                continue
            project_data = {
                "name": safe_inner_text(project.query_selector("//span[@class='new-project-experiences__item-name']")),
                "duration": safe_inner_text(
                    project.query_selector("//div[@class='new-project-experiences__item-time']")),
                "description": safe_inner_text(
                    project.query_selector("//span[@class='new-project-experiences__item-desc']"))
            }
            if any(project_data.values()):
                resume_data["project_experiences"].append(project_data)
    except Exception as e:
        print(f"Error extracting project experiences: {e}")

    # Extract Education
    try:
        education_items = page.query_selector_all("//div[@class='new-education-experiences__item']") or []
        for education in education_items:
            education_data = {
                "institution": safe_inner_text(
                    education.query_selector("//span[@class='new-education-experiences__item-name']")),
                "duration": safe_inner_text(
                    education.query_selector("//span[@class='new-education-experiences__item-time']")),
                "major": safe_inner_text(
                    education.query_selector("//div[@class='new-education-experiences__item-bottom']//span[1]")),
                "degree": safe_inner_text(
                    education.query_selector("//div[@class='new-education-experiences__item-bottom']//span[2]"))
            }
            if any(education_data.values()):
                resume_data["education"].append(education_data)
    except Exception as e:
        print(f"Error extracting education: {e}")

    # Extract Personal Advantages
    try:
        resume_data["personal_advantages"] = safe_inner_text(
            wait_for_selector("//span[@class='resume-section-self-evaluation']"))
    except Exception as e:
        print(f"Error extracting personal advantages: {e}")

    # Extract Training Experiences
    try:
        training_items = page.query_selector_all("//div[@class='new-training-experiences__item']") or []
        for training in training_items:
            training_data = {
                "institution": safe_inner_text(
                    training.query_selector("//span[@class='new-training-experiences__item-name']")),
                "duration": safe_inner_text(
                    training.query_selector("//span[@class='new-training-experiences__item-time']")),
                "course": safe_inner_text(
                    training.query_selector("//div[@class='new-training-experiences__item-bottom']"))
            }
            if any(training_data.values()):
                resume_data["training_experiences"].append(training_data)
    except Exception as e:
        print(f"Error extracting training experiences: {e}")

    # Extract Personal Works
    try:
        personal_works = page.query_selector_all("//div[@class='resume-accessory-newest__group']") or []
        for work in personal_works:
            work_data = {
                "title": safe_inner_text(work.query_selector("//div[@class='resume-accessory-newest__group-title']")),
                "image_url": safe_get_attribute(
                    work.query_selector("//div[@class='resume-accessory-newest__img']//img"), "src")
            }
            if any(work_data.values()):
                resume_data["personal_works"].append(work_data)
    except Exception as e:
        print(f"Error extracting personal works: {e}")

    try:
        # ... existing personal info extraction code ...

        # 添加沟通职位提取（在个人信息提取部分的最后）
        communication_position_element = wait_for_selector("//div[@class='three-session-panel']//div[@class='im-three-list__panel--job--title is-ellipsis']")
        communication_position_text = safe_inner_text(communication_position_element)
        resume_data["personal_info"]["communication_position"] = communication_position_text.strip() if communication_position_text else ""

    except Exception as e:
        print(f"Error extracting personal info: {e}")

    return resume_data

def check_exist(page):
    """检查页面是否存在且已登录"""
    if not page:
        file_logger.log("页面对象为空")
        return False
        
    try:
        # 尝试刷新页面状态，不使用timeout参数
        page.wait_for_load_state("domcontentloaded")
        # 获取当前URL
        current_url = page.url
        file_logger.log(f"当前页面URL: {current_url}")
        
        # 如果URL包含login，表示未登录
        if "login" in current_url:
            file_logger.log("检测到登录页面，用户未登录")
            return False
            
        # 检查是否有登录后才会出现的元素
        try:
            # 尝试查找登录后才会出现的元素，如导航栏，不使用timeout参数
            nav_exists = page.query_selector(".app-nav__item") is not None
            if not nav_exists:
                file_logger.log("未找到导航栏元素，可能未登录")
                return False
        except Exception as e:
            file_logger.log(f"检查导航栏元素时出错: {e}")
            # 继续执行，不因为这个错误就判断为未登录
        
        return True
    
    except Exception as e:
        file_logger.error(f"检查页面存在时出错: {str(e)}")
        return False

def extract_user_info(user_locator):
    """
    从 Locator 对象中提取用户信息并生成唯一ID
    参数:
        user_locator: Playwright Locator 对象，表示一个用户条目
    返回:
        str: 用户的唯一ID (MD5)
    """
    # 提取姓名
    name_locator = user_locator.locator(".im-session-item__name-title")
    name = name_locator.text_content().strip() if name_locator.count() > 0 else "未知姓名"

    # 提取职位（添加容错机制）
    position_locator = user_locator.locator(".im-session-item-subtitle__suffix")
    position = position_locator.text_content().strip() if position_locator.count() > 0 else "未知职位"

    # 提取头像图片URL
    avatar_locator = user_locator.locator(".km-image__inner")
    avatar_img = avatar_locator.get_attribute("src") if avatar_locator.count() > 0 else "未知头像"

    # 拼接字符串
    combined_string = f"{name}_{position}_{avatar_img}"
    # 生成MD5值
    md5_hash = hashlib.md5(combined_string.encode('utf-8')).hexdigest()


    return md5_hash,name


def extract_user_info2(user_locator):
    """
    从 ElementHandle 对象中提取用户信息并生成唯一ID
    参数:
        user_locator: Playwright ElementHandle 对象，表示一个用户条目
    返回:
        str: 用户的唯一ID (MD5)
        str: 用户名
    """
    # 提取姓名
    name_element = user_locator.query_selector(".im-session-item__name-title")
    name = name_element.text_content().strip() if name_element else "未知姓名"
    # 提取职位（添加容错机制）
    position_element = user_locator.query_selector(".im-session-item-subtitle__suffix")
    position = position_element.text_content().strip() if position_element else "未知职位"
    # 提取头像图片URL
    avatar_element = user_locator.query_selector(".km-image__inner")
    avatar_img = avatar_element.get_attribute("src") if avatar_element else "未知头像"
    # 拼接字符串
    combined_string = f"{name}_{position}_{avatar_img}"
    # 生成MD5值
    md5_hash = hashlib.md5(combined_string.encode('utf-8')).hexdigest()
    return md5_hash, name


def is_empty(data):
    if not data:
        return True
    if len(data) < 1:
        return True
    return False


def get_last_me(messages):
    if messages is None:
        return None
    last_me = next((msg for msg in reversed(messages) if msg["type"] == "我方"), None)
    return last_me


def get_me_num(messages):
    if messages is None:
        return 0
    filtered_messages = [msg for msg in messages if msg["type"] in ["我方"]]
    return len(filtered_messages)

def get_you_num(messages):
    if messages is None:
        return 0
    filtered_messages = [msg for msg in messages if msg["type"] in ["对方"]]
    return len(filtered_messages)

def get_you_wechat(messages):
    if messages is None:
        return False
    filtered_messages = [msg['text'] for msg in messages if msg["type"] in ["对方"]]
    if "我已同意交换微信，期待回复" in filtered_messages:
        return True
    return False


def get_last_who(messages):
    if messages is None:
        return None
    filtered_messages = [msg for msg in messages if msg["type"] in ["我方", "对方"]]
    if filtered_messages:
        last_message = filtered_messages[-1]
        return last_message
    else:
        return None

def parse_chat_messages(page):
    """解析聊天记录并按顺序标注消息来源"""
    messages = []
    # 定位所有消息容器
    PlaywrightUtils.wait_for_element(page,'//div[@class="km-list__item im-message"]')
    items = page.locator('xpath=//div[@class="km-list__item im-message"]').all()


    for i, item in enumerate(items):
        entry = {
            "time": None,
            "text": None,
            "type": None,
            "ar": None
        }

        # 提取时间戳
        time_element = item.locator('xpath=.//div[contains(@class, "im-message__time")]')
        if time_element.count() > 0:
            entry["time"] = time_element.first.text_content()

        # 提取消息内容（兼容不同类型）
        text_element = item.locator('xpath=.//div[@class="im-message__text"]')
        if text_element.count() > 0:
            entry["text"] = text_element.first.text_content()
        else:
            # 检查系统消息
            toast_element = item.locator('xpath=.//div[contains(@class, "im-message__toast-inner")]')
            if toast_element.count() > 0:
                entry["text"] = toast_element.first.text_content()
            # 检查微信请求
            wx_request = item.locator('xpath=.//div[contains(@class, "imc-wx-request__title--text--title")]')
            if wx_request.count() > 0:
                entry["text"] = wx_request.first.text_content()

        # 提取已读状态
        read_element = item.locator('xpath=.//div[@class="im-message__read-state is-read"]')
        if read_element.count() > 0:
            entry["ar"] = "已读"

        # 判断消息类型
        if item.locator('xpath=.//div[contains(@class, "im-message__bubble--me")]').count() > 0:
            entry["type"] = "我方"
        elif item.locator('xpath=.//img[contains(@src, "avatar_m2.png")]').count() > 0:
            entry["type"] = "我方"
        elif item.locator('xpath=.//img[contains(@src, "mypics.zhaopin.cn/avatar/")]').count() > 0:
            entry["type"] = "对方"
        elif item.locator('xpath=.//div[contains(@class, "imc-job-toast")]').count() > 0:
            entry["type"] = "系统"
        else:
            entry["type"] = "未知"

        # 仅添加有意义的条目
        if entry["time"] or entry["text"] or entry["ar"]:
            messages.append(entry)

    return messages

def create_response_function(dt):
    if dt is None:
        return None
    data = eval(dt)

    # 根据输入数据生成键值对字典
    response_dict = {}
    for item in data:
        keys = item["key"].split()  # 将key按空格分割成列表
        value = item["value"]
        # 将每个key映射到对应的value
        for key in keys:
            response_dict[key] = value

    # 定义并返回处理函数
    def get_response(sentence,is_w):
        # 检查输入句子中是否包含字典中的某个key
        for key in response_dict:
            if key in sentence:
                return response_dict[key]
        # 如果没有匹配到任何key，返回默认回复
        if is_w:
            return "方便的话，我们微信沟通!"
        else:
            return "可以加个联系方式详细聊聊吗？"

    return get_response


def safe_query(element, selector, default=""):
    node = element.query_selector(selector)
    return node.inner_text().strip() if node else default

def is_current_time_in_range(data):
    if not data:
        return False

    # 获取当前时间
    now = datetime.now()
    current_hour = now.hour
    current_minute = now.minute

    # 检查每个item
    for item_key in ['item1', 'item2', 'item3']:
        item = data[item_key]

        # 如果toggle为True才进行检查
        if item['toggle']:
            start_hour = int(item['startHour'])
            start_minute = int(item['startMinute'])
            end_hour = int(item['endHour'])
            end_minute = int(item['endMinute'])

            # 将时间转换为分钟数便于比较
            current_total_minutes = current_hour * 60 + current_minute
            start_total_minutes = start_hour * 60 + start_minute
            end_total_minutes = end_hour * 60 + end_minute

            # 检查当前时间是否在范围内
            # 考虑跨天情况
            if start_total_minutes <= end_total_minutes:
                # 不跨天
                if start_total_minutes <= current_total_minutes <= end_total_minutes:
                    return True
            else:
                # 跨天情况
                if current_total_minutes >= start_total_minutes or current_total_minutes <= end_total_minutes:
                    return True

    return False

def get_fl_content(data, is_read=1):
    # 获取当前时间
    now = datetime.now()
    current_hour = now.hour
    current_minute = now.minute
    # 检查每个item
    for item_key in ['item1', 'item2', 'item3']:
        item = data[item_key]

        # 如果toggle为True才进行检查
        if item['toggle']:
            start_hour = int(item['startHour'])
            start_minute = int(item['startMinute'])
            end_hour = int(item['endHour'])
            end_minute = int(item['endMinute'])
            messages = []

            if is_read == 1:
                readMessage1 = item['readMessage1']
                if readMessage1 and readMessage1 != "":
                    messages.append(readMessage1)
                readMessage2 = item['readMessage2']
                if readMessage2 and readMessage2 != "":
                    messages.append(readMessage2)
                readMessage3 = item['readMessage3']
                if readMessage3 and readMessage3 != "":
                    messages.append(readMessage3)
            else:
                unreadMessage1 = item['unreadMessage1']
                if unreadMessage1 and unreadMessage1 != "":
                    messages.append(unreadMessage1)
                unreadMessage2 = item['unreadMessage2']
                if unreadMessage2 and unreadMessage2 != "":
                    messages.append(unreadMessage2)
                unreadMessage3 = item['unreadMessage3']
                if unreadMessage3 and unreadMessage3 != "":
                    messages.append(unreadMessage3)

            # 将时间转换为分钟数便于比较
            current_total_minutes = current_hour * 60 + current_minute
            start_total_minutes = start_hour * 60 + start_minute
            end_total_minutes = end_hour * 60 + end_minute

            # 检查当前时间是否在范围内
            # 考虑跨天情况
            if start_total_minutes <= end_total_minutes:
                # 不跨天
                if start_total_minutes <= current_total_minutes <= end_total_minutes:
                    # todo 随机有点问题
                    if len(messages) == 0:
                        return None
                    return random.choice(messages)
            else:
                # 跨天情况
                if current_total_minutes >= start_total_minutes or current_total_minutes <= end_total_minutes:
                    if len(messages) == 0:
                        return None
                    return random.choice(messages)

    return None


class PlaywrightWorker(QThread):
    status_signal = pyqtSignal(str)  # 用于反馈状态

    def __init__(self):
        super().__init__()
        self.status = "idle"  # 当前状态
        self.running = True  # 控制线程运行
        self.paused = False  # 新增暂停标志
        self.pause_condition = QWaitCondition()  # 用于暂停/继续控制
        self.mutex = QMutex()  # 互斥锁
        self.browser = None
        self.page = None
        self.is_debug = False
        self.gt_all_data = None
        self.continue_say_hallo = False
        self.response_func = None
        self.dzh_success = False
        self.last_reset_date = datetime.now().date()  # 记录上次重置日期
        self.db_path = config.get_db_path()
        self.playwright = None  # 添加playwright属性
        self.browser_ws_endpoint = None  # 保存浏览器WebSocket端点
        self.init_data()

        self.resource_monitor = ResourceMonitor()
        self.browser_restart_count = 0
        self.max_browser_restarts = 3  # 最大重启次数
        self.last_browser_restart = datetime.now()
        self.browser_restart_interval = timedelta(hours=4)  # 4小时重启一次
        self.error_count = 0
        self.max_errors = 3  # 最大错误次数

    def check_and_reset_daily_status(self):
        """检查是否需要重置每日状态"""
        current_date = datetime.now().date()
        if current_date > self.last_reset_date:
            file_logger.log(f"检测到新的一天，重置dzh_success状态。上次重置日期: {self.last_reset_date}, 当前日期: {current_date}")
            self.dzh_success = False
            self.last_reset_date = current_date
            file_logger.log("dzh_success已重置为False")
            return True
        return False
    
    def init_data(self):
        """初始化数据"""
        try:
            file_logger.log("初始化数据")
            # 加载配置数据
            self.load_re_data()
            self.gt_all_data = gtDb.get_data()
            self.response_func = create_response_function(gtDb.get_json_value("keyword"))

            file_logger.log("数据初始化完成")
            return True
        except Exception as e:
            file_logger.error(f"初始化数据时出错: {e}")
            return False
    def is_start(self):
        return self.status == "start"
    
    def pause(self):
        """暂停线程执行"""
        self.mutex.lock()
        self.paused = True
        self.status = "paused"
        
        # 保存当前页面URL
        if self.page:
            try:
                self.last_url = self.page.url
                file_logger.log(f"暂停时保存URL: {self.last_url}")
            except Exception as e:
                file_logger.error(f"保存URL时出错: {e}")
        
        self.mutex.unlock()
        self.status_signal.emit("已暂停")
        file_logger.log("线程已暂停")
    # 不再使用条件变量等待，让线程继续运行
    
    def resume(self):
        """恢复线程执行"""
        self.mutex.lock()
        self.paused = False
        self.status = "start"
        self.mutex.unlock()
        self.status_signal.emit("已恢复")
        file_logger.log("线程已恢复")
        
        # 不再尝试检查页面有效性或重新连接浏览器
        # 只是简单地改变状态标志，让线程继续执行实际操作

    def stop(self):
        """停止线程执行"""
        self.mutex.lock()
        self.running = False
        self.paused = False
        self.status = "stop"
        self.mutex.unlock()
        self.status_signal.emit("已停止")
        file_logger.log("线程已停止")
    
    def sleep(self, tm):
        self.page.wait_for_timeout(tm*1000)

    @handle_exception
    def run(self):
        self.continue_say_hallo = False
        file_logger.log("启动PlaywrightWorker线程")
        try:
            # 先检查本地Chrome浏览器
            self.playwright = sync_playwright().start()
            p = self.playwright
            if not self.browser:
                self.launch_browser(p)
                self.init_data()

            while self.running:

                if self.resource_monitor.should_cleanup():
                    self.resource_monitor.force_cleanup()
                
                # 检查是否需要重启浏览器
                # if self.should_restart_browser():
                #     file_logger.log("达到浏览器重启条件，准备重启")
                #     self.restart_browser()
                #     continue

                self.mutex.lock()
                is_paused = self.paused
                self.mutex.unlock()
                # 处于暂停状态
                if is_paused:
                    self.sleep(1)
                    continue

                # 如果不是开始的状态
                if not self.is_start():
                    self.sleep(1)
                    continue

                # # 判断是不是登录状态，如果不是过
                if not check_exist(self.page):
                    file_logger.log("非登录状态，等待登录")
                    time.sleep(1)
                    continue

                try:
                    file_logger.log("开始body")
                    self.process_body()
                    file_logger.log("process_body方法执行完成")
                    self.error_count = 0  # 成功执行，重置错误计数
                except Exception as e:
                    self.error_count += 1
                    file_logger.error(f"执行process_body时出错(第{self.error_count}次): {e}")
                    if "Target page, context or browser has been closed" in str(e):
                        file_logger.log("页面已关闭，尝试重新初始化", "WARNING")
                        print("页面关闭了，尝试重新初始化")
                        # 尝试重新初始化浏览器
                        try:
                            if self.browser:
                                try:
                                    self.browser.close()
                                except:
                                    pass
                            self.browser = None
                            self.page = None
                            self.launch_browser(self.playwright)
                            file_logger.log("浏览器重新初始化成功")
                        except Exception as reinit_error:
                            file_logger.error(f"重新初始化浏览器失败: {reinit_error}")
                        self.sleep(5)
                        continue
                    if self.error_count >= self.max_errors:
                        file_logger.log(f"连续错误{self.max_errors}次，重启浏览器")
                        self.restart_browser()
                        self.error_count = 0
                        continue

                    file_logger.error(f"执行process_body时出错: {e}", exc_info=True)
                    self.sleep(min(10, self.error_count * 2))  # 递增等
                    continue
                    
        
        
            try:
                if self.browser:
                    file_logger.log("关闭浏览器")
                    self.browser.close()
                    self.browser = None
                    self.page = None
            except Exception as e:
                file_logger.error(f"关闭浏览器时出错: {e}")
                
            try:
                if self.playwright:
                    file_logger.log("关闭playwright")
                    self.playwright.stop()
                    self.playwright = None
            except Exception as e:
                file_logger.error(f"关闭playwright时出错: {e}")  
                    
        except Exception as e:
            file_logger.error(f"运行时发生严重错误: {e}", exc_info=True)
        finally:
            # 确保资源释放
            if self.browser:
                try:
                    file_logger.log("关闭浏览器")
                    self.browser.close()
                except Exception as e:
                    file_logger.error(f"关闭浏览器时出错: {e}")
                self.browser = None
                self.page = None
            if hasattr(self, 'playwright') and self.playwright:
                try:
                    self.playwright.stop()
                except:
                    pass
            self.quit()  # 终止线程

    def should_restart_browser(self):
        """判断是否需要重启浏览器"""
        now = datetime.now()
        memory_usage = self.resource_monitor.check_memory_usage()
        
        # 条件1: 定时重启（每4小时）
        time_condition = now - self.last_browser_restart > self.browser_restart_interval
        
        # 条件2: 内存使用过高
        memory_condition = memory_usage > 95
        
        # 条件3: 重启次数未超限
        restart_condition = self.browser_restart_count < self.max_browser_restarts
        
        return (time_condition or memory_condition) and restart_condition
    

    def restart_browser(self):
        return
        """重启浏览器"""
        try:
            file_logger.log("开始重启浏览器")
            
            # 关闭现有浏览器和上下文
            if self.context:
                try:
                    self.context.close()
                    file_logger.log("已关闭浏览器上下文")
                except Exception as e:
                    file_logger.log(f"关闭上下文时出错: {e}")
            
            if self.browser:
                try:
                    self.browser.close()
                    file_logger.log("已关闭浏览器")
                except Exception as e:
                    file_logger.log(f"关闭浏览器时出错: {e}")
            
            # 强制清理内存
            self.resource_monitor.force_cleanup()
            time.sleep(3)  # 等待资源释放
            
            # 重置引用
            self.browser = None
            self.page = None
            self.context = None
            
            # 使用现有的 playwright 实例重新启动浏览器
            if self.playwright:
                launch_success = self.launch_browser(self.playwright)
                if launch_success:
                    # 重要：launch_browser 使用 launch_persistent_context
                    # 所以 browser 应该设置为 context（这是 persistent context 的特性）
                    self.browser = self.context
                    
                    # 调用初始化数据方法
                    self.init_data()
                    
                    self.browser_restart_count += 1
                    self.last_browser_restart = datetime.now()
                    file_logger.log(f"浏览器重启成功(第{self.browser_restart_count}次)")
                    return True
                else:
                    file_logger.error("launch_browser 返回失败")
                    return False
            else:
                file_logger.error("playwright 实例不存在，无法重启浏览器")
                return False
                
        except Exception as e:
            file_logger.error(f"重启浏览器时出错: {e}")
            # 如果重启失败，尝试重置状态
            self.browser = None
            self.page = None
            self.context = None
            return False
    
    
    def process_body(self):
        # 这个开始,先判断有没有新消息

        try:
            self.page.evaluate("""
            () => {
                // 处理通用弹窗
                const popups = document.querySelectorAll('.km-modal__wrapper, .image-popup-modal');
                popups.forEach(popup => {
                    if (popup.offsetParent !== null) {
                        const closeBtn = popup.querySelector('.km-modal__close-btn, .close');
                        if (closeBtn) {
                            closeBtn.click();
                        } else {
                            popup.style.display = 'none';
                        }
                    }
                });
                
                // 专门处理 e.html 类型的弹窗
                const modalBodies = document.querySelectorAll('.km-modal__body');
                modalBodies.forEach(modal => {
                    if (modal.offsetParent !== null) {
                        const closeBtn = modal.querySelector('.close');
                        const coverImg = modal.querySelector('.cover img');
                        // 如果包含特定的图片URL，确认是登录后的弹窗
                        if (closeBtn && coverImg && coverImg.src.includes('zhaopin.cn/mofang')) {
                            console.log('检测到登录后弹窗，自动关闭');
                            closeBtn.click();
                        }
                    }
                });
            }
            """)
        except Exception as e:
            file_logger.log(f"弹窗清理检查失败: {e}")

        if not self.page:
            file_logger.log("页面不存在，重新启动")
            self.launch_browser(self.playwright)
            return
        self.check_and_reset_daily_status()
        file_logger.log("检查是否有未读消息")
        is_unread = self.check_unread_messages()

        if is_unread:
            file_logger.log("有未读消息，准备处理")
            logger.save_log("gt", "发现待沟通内容")
            try:
                file_logger.log("点击进入聊天页面")
                ClickUtils.click_and_wait_url_change(self.page, "//a[@href='/app/im']",['app/im'])
                self.process_unread_messages()
                file_logger.log("未读消息处理完成")
            except Exception as e:
                file_logger.error(f"执行process_unread_messages时出错: {e}", exc_info=True)
                print("出错了", e)
                # 这里表示服务断了，直接停止整个程序
            return
        file_logger.log("开始检查是否需要复聊")
        # 判断是不是再复聊时间，如果再进入复聊，别的不再进行。
        is_re = self.is_re_say_hello()
        if is_re:
            # 进入复聊
            file_logger.log("在复聊时间范围内，准备进行复聊")
            ClickUtils.click_and_wait_url_change(self.page, "//a[@href='/app/im']", ['app/im'])
            file_logger.log("开始执行复聊流程")
            self.process_fl()
            file_logger.log("复聊流程执行完成")
            return

        # 打招呼逻辑处理
        # 先拿列表
        file_logger.log("检查是否需要打招呼")
        if self.dzh_success:
            file_logger.log("dzh_success为True，打招呼已全部完成，等待2秒后返回")
            self.sleep(2)
            return
        # file_logger.log("获取今日打招呼次数")
        # max_cs = sjudb.get_today_cs_sum()
        # if max_cs >= 200:
        #     file_logger.log(f"今日打招呼次数已达上限: {max_cs}，返回")
        #     return
        file_logger.log("获取tableData数据")
        tt_data = shDb.get_json_value("tableData")
        if not tt_data:
            file_logger.log("tableData数据为空，返回")
            return
        tt_data = eval(tt_data)
        file_logger.log("获取下一个职位编号")
        zwno = sjudb.get_next_zwno(tt_data)
        if not zwno:
            file_logger.log("没有获取到下一个职位编号，返回")
            self.dzh_success = True
            return

        file_logger.log(f"开始处理职位编号: {zwno}的打招呼")
        # 点击筛选条件(todo 0406 如果和没打完的是通过一个，不进行筛选， 点击筛选条件快一点。 沟通慢一点)
        if not self.continue_say_hallo:
            cp = self.handle_greeting(zwno)
            if not cp:
                return
        # 针对列表开始打招呼
        file_logger.log("开始执行打招呼")
        self.say_hello(zwno)
        file_logger.log("打招呼执行完成")

    def is_re_say_hello(self):
        # 是不是复聊，拿数据判断
        is_re_data = self.load_re_data()
        return is_current_time_in_range(is_re_data)

    def get_current_zh_zwno(self):
        # 查看当前第几个打招呼的
        current_zh = sjudb.get_last_record_of_day()

        if not current_zh:
            # 没有数据，就创建第一个招呼
            sjudb.get_or_create_record(1)
            current_zh = sjudb.get_last_record_of_day()

        # 拿到id
        zwno = current_zh.get("zwno")
        greetingFrequency = shDb.get_json_value("settings.greetingFrequency")
        if greetingFrequency and len(greetingFrequency) > 0:
            greetingFrequency = int(greetingFrequency.replace("分钟一次", ""))
        else:
            greetingFrequency = 20

        time_out = sjudb.is_over_20_minutes(zwno, greetingFrequency)
        # 如果超时了
        if time_out:
            # 标记完成，并且自动+1
            if not current_zh.get("complate"):
                sjudb.mark_complete(zwno)
                zwno += 1
            else:
                zwno += 1
        else:
            # 如果没超时，完成了，等待
            if current_zh.get("complate"):
                return None

        return zwno


    def process_fl(self):

        is_hd = check_hd_page(self.page)
        if not is_hd:
            file_logger.log("不是聊天页面，返回")
            return

        # 判断check
        elet = PlaywrightUtils.wait_for_element(self.page,'div.km-checkbox:has(span:has-text("未读"))')
        if elet is not None:
            checkbox = self.page.locator('div.km-checkbox:has(span:has-text("未读"))')

            # 双重状态检测机制
            is_checked = (
                    "km-checkbox--checked" in checkbox.get_attribute("class")  # 通过类名检测
                    or checkbox.locator("input[type=checkbox]").is_checked()  # 通过原生属性检测
            )

            if is_checked:
                # 带防御性等待的点击
                checkbox.click(timeout=5000)
                print("已勾选未读过滤")
            else:
                checkbox.click(timeout=5000)
                self.page.wait_for_timeout(1000)
                checkbox.click(timeout=5000)


        logger.save_log("fl", "开始复聊")
        data_list = fcluDb.get_data_list()

        # 判断是不是有信息
        is_re_data = self.load_re_data()
        
        max_num = is_re_data.get("settings", {}).get("ag", 0)
        PlaywrightUtils.wait_for_element(self.page,".im-session-list__virtual--box")
        sidebar = self.page.locator(".im-session-list__virtual--box")
        while True:
            items = sidebar.locator(".im-session-item").all()
            if len(items) == 0:
                file_logger.log("没有要复聊的内容了")
                print('没有要复聊的内容!')
                return

            # 有信息开始处理
            for item in items:
                if not self.is_start():
                    file_logger.log("不在运行状态，返回")
                    return
                is_re = self.is_re_say_hello()
                if not is_re:
                    file_logger.log("不在复聊时间范围内，返回")
                    return

                reminder = item.locator('.session-item-wrapper__reminder').count()
                if reminder > 0:
                    # 检查具体文本内容，确保是目标提醒
                    reminder_text = item.locator('.session-item-wrapper__reminder span').inner_text()
                    if "列表数据最多保留90天，已全部加载完毕" in reminder_text:
                        file_logger.log("已加载完所有数据，返回")
                        # 怎么标记已完成
                        return  # 停止循环

                pass_name_list = ['猜你喜欢','智联小秘书','快速处理新招呼']
                # 循环事件的问题,聊完天会变到上边去,比较麻烦
                _, current_name = extract_user_info(item)
                if current_name in pass_name_list:
                    continue

                # 如果不可见，滚动到可见,并且点击项目
                scroll_view_and_click(item, self.page)
                # 工作标题

                current_id = get_session_id(self.page)
                print(f"当前用户: {current_name}, 当前id: {current_id}")

                if data_list.count(current_id) > 0:
                    file_logger.log(f"已处理过用户: {current_name}，跳过")
                    continue

                data_list.append(current_id)
                # 处理知道了的异常
                try:
                    exists = self.page.query_selector(
                    "//div[@class='km-modal__title-inner' and normalize-space(text())='求职者存在违规行为，系统已为您自动屏蔽']") is not None
                    if exists:
                        file_logger.log("求职者存在违规行为，系统已为您自动屏蔽")
                        humanized_click(self.page,"//button[@class='km-button km-control km-ripple-off km-button--light km-button--outlined default-button' and //div[normalize-space(text())='知道了']]")
                except:
                    pass

                PlaywrightUtils.wait_for_element(self.page,'//div[@class="km-list__item im-message"]')
                try:
                    chat_history = parse_chat_messages(self.page)
                    print(chat_history)

                except Exception as e:
                    print("解析消息出错", e)
                    file_logger.error(f"解析消息时出错: {e}", exc_info=True)
                    continue

                if len(chat_history) == 0:
                    file_logger.log("消息为空，跳过")
                    continue

                pi = self.get_people_info()
                # 限制年龄
                if not self.gt_all_data.get("limitAge"):
                    if self.gt_all_data.get("limitAgeText", {}).get("start"):
                        if int(pi.get("age")) < int(self.gt_all_data.get("limitAgeText", {}).get("start")):
                            logger.save_log("fl",
                                            "{} 年龄为 {}, 不符合要求".format(pi.get("name"), pi.get("age")))
                            if self.gt_all_data.get("pass", {}).get("age") and len(
                                    self.gt_all_data.get("pass", {}).get("age")) > 0:
                                # 点击不合适
                                logger.save_log("fl", "和 {} 沟通 年龄不合适发送了 {}".format(pi.get("name"),
                                                                                              self.gt_all_data.get(
                                                                                                  "pass",
                                                                                                  {}).get(
                                                                                                  "age")))

                            continue
                    if self.gt_all_data.get("limitAgeText", {}).get("end"):
                        if int(pi.get("age")) > int(self.gt_all_data.get("limitAgeText", {}).get("end")):
                            logger.save_log("fl",
                                            "{} 年龄为 {}, 不符合要求".format(pi.get("name"), pi.get("age")))
                            if self.gt_all_data.get("pass", {}).get("age") and len(
                                    self.gt_all_data.get("pass", {}).get("age")) > 0:
                                logger.save_log("fl", "和 {} 沟通 年龄不合适发送了 {}".format(pi.get("name"),
                                                                                              self.gt_all_data.get(
                                                                                                  "pass",
                                                                                                  {}).get(
                                                                                                  "age")))

                            continue
                # 性别
                if not self.gt_all_data.get("limitSex"):
                    if pi.get("sex") == '':
                        logger.save_log("fl", "{} 性别位置 不符合要求".format(pi.get("name")))

                        continue
                    if self.gt_all_data.get("limitSexText", None):
                        if pi.get("sex") != self.gt_all_data.get("limitSexText"):
                            logger.save_log("fl",
                                            "{} 性别为 {}, 不符合要求".format(pi.get("name"), pi.get("sex")))
                            if self.gt_all_data.get("pass", {}).get("sex") and len(
                                    self.gt_all_data.get("pass", {}).get("sex")) > 0:
                                logger.save_log("fl", "和 {} 沟通 性别不合适发送了 {}".format(pi.get("name"),
                                                                                              self.gt_all_data.get(
                                                                                                  "pass",
                                                                                                  {}).get(
                                                                                                  "sex")))
                            continue
                # 学历
                if not self.gt_all_data.get("limitXl"):
                    if self.gt_all_data.get("limitXlList", None):
                        if pi.get("xl") not in self.gt_all_data.get("limitXlList"):
                            logger.save_log("fl",
                                            "{} 学历为 {}, 不符合要求".format(pi.get("name"), pi.get("xl")))
                            if self.gt_all_data.get("pass", {}).get("xl") and len(
                                    self.gt_all_data.get("pass", {}).get("xl")) > 0:
                                logger.save_log("fl", "和 {} 沟通 学历不合适发送了 {}".format(pi.get("name"),
                                                                                              self.gt_all_data.get(
                                                                                                  "pass",
                                                                                                  {}).get(
                                                                                                  "xl")))
                            continue

                # 获取简历
                self.get_resume_by_page(chat_history,pi)


                # 判断一下，拒绝过的就不复聊了
                last_other_message = next((msg for msg in reversed(chat_history) if msg["type"] == "对方"), None)
                
                if last_other_message and last_other_message["text"]:
                    file_logger.log(f"用户 {current_name} 的最后消息: {last_other_message['text']}")
                    # 定义拒绝的关键词列表
                    rejection_keywords = ["不考虑", "不好意思", "不合适", "不太合适", "不适合", "不太适合", 
                                         "不想了解", "不感兴趣", "谢谢", "再见", "拒绝", "不用了", 
                                         "婉拒", "抱歉", "打扰了", "告辞", "傻", "举报", "滚", 
                                         "烦人", "骚扰", "草", "你妈"]
                    
                    # 检查是否包含拒绝关键词
                    if any(keyword in last_other_message["text"] for keyword in rejection_keywords):
                        file_logger.log(f"用户 {current_name} 的最后消息包含拒绝关键词，跳过复聊")
                        fcluDb.insert_fl_cur_log(current_id, current_name, "FALSE")
                        # 标记不合适
                        humanized_click(self.page,
                                        "//a[@class='km-button km-control km-ripple-off km-button--light km-button--outlined' and .//span[normalize-space(text())='不合适']]")
                        file_logger.log(f"用户 {current_name} 标记不合适")
                        continue


                last_who = get_last_who(chat_history)
                if not last_who:
                    # 没有我方和对方数据
                    file_logger.log("没有我方和对方数据，跳过复聊，写入数据库")
                    fcluDb.insert_fl_cur_log(current_id,current_name,"FALSE")
                    continue
                # if last_who['type'] == "对方":
                #     file_logger.log("最后一条消息是对方的，跳过复聊，写入数据库")
                #     fcluDb.insert_fl_cur_log(current_id,current_name,"FALSE")
                #     continue


                # 获取要发送的内容
                res = get_fl_content(is_re_data, 1 if last_who['ar'] is not None else 0)
                if not res:
                    file_logger.log("没有要发送的内容，跳过复聊，写入数据库")
                    fcluDb.insert_fl_cur_log(current_id,current_name,"FALSE")
                    continue

                me_num = get_me_num(chat_history)
                if int(me_num) >= int(max_num):
                    file_logger.log("超过了复聊次数，跳过复聊，写入数据库")
                    print("超过了复聊次数")
                    fcluDb.insert_fl_cur_log(current_id,current_name,"FALSE")
                    continue
#11111
                textarea = self.page.wait_for_selector(
                    'xpath=//textarea[contains(@class, "km-input__original")]',
                    timeout=15000
                )
                # 先聚焦元素（触发可能的验证逻辑）
                textarea.focus()
                # 直接填充内容（立即生效）
                # textarea.fill(res)
                self.page.keyboard.type(res)
                fcluDb.insert_fl_cur_log(current_id, current_name, "TRUE")
                self.sleep(1)
                # 按回车键
                if not self.is_debug:
                    self.page.keyboard.press('Enter')
                logger.save_log("fl", "给{}发送复聊信息".format(self.get_name()))
                file_logger.log("给{}发送复聊信息".format(self.get_name()))

                if not check_jj(chat_history,"我想与您交换微信，方便沟通"):
                    try:
                        humanized_click(self.page,
                                        "//a[@class='km-button km-control km-ripple-off km-button--light km-button--outlined' and .//span[normalize-space(text())='换微信']]")
                        humanized_click(self.page,
                                        "//div[@class='km-popover__inner']//button[@class='km-button km-control km-ripple-off km-button--primary km-button--filled' and div[normalize-space(text())='确定']]")

                    except Exception as e:
                        humanized_click(self.page,
                                        "//div[@class='km-popover__inner']//button[@class='km-button km-control km-ripple-off km-button--primary km-button--filled' and div[normalize-space(text())='我知道了']]")
                        print("换微信出错", e)
# 11111
                self.page.wait_for_timeout(1000)

            scroll_container = self.page.query_selector(".im-session-list__virtual")
            self.page.evaluate("el => el.scrollBy(0, 500)", scroll_container)
            self.page.wait_for_timeout(3000)

    def has_nummmm(self):
        try:
            element = self.page.query_selector("//div[@class='renewal-rights-chat']//span")
            text = element.text_content() if element else None
            file_logger.log("打招呼剩余次数 {}".format(text))
            if text:
                if int(text) == 0:
                    return False
                else:
                    return True
        except:
            pass
        return True
    def say_hello(self, zwno):

        cg = check_gt_page(self.page)
        if not cg:
            ck = ClickUtils.click_and_wait_url_change(self.page,
                                                      "//a[@href='/app/recommend' and span[text()='推荐人才']]",
                                                      ['app/recommend'])
            if not ck:
                file_logger.log("点击页面失败")
                return False
            cg = check_gt_page(self.page)
            if not cg:
                file_logger.log("页面不对，出去重新进")
                return
        self.continue_say_hallo = False
        file_logger.log(f"开始执行say_hello，职位编号: {zwno}")
        l_data = self.load_data()
        if not l_data:
            file_logger.log("没有打招呼数据....")
            self.sleep(1)
        time_sleep = 0
        change_wechat = True
        try:
            file_logger.log("dddd: {}".format(l_data))
            freq = l_data.get("settings",{}).get("greetingFrequency")
            change_wechat = l_data.get("exchangeContact",True)
            time_sleep = int(freq.replace("分钟一次","")) * 60
        except Exception as e:
            pass

        try:
            file_logger.log("等待列表项元素出现")
            PlaywrightUtils.wait_for_element(self.page, "//div[@role='listitem']")
            separator_xpath = "//p[contains(text(), '更多相关人才')]/ancestor::div[@role='listitem']"
            
            # 检查是否存在分隔符
            separator_exists = self.page.query_selector(separator_xpath) is not None
            
            if separator_exists:
                # 如果存在分隔符，只获取分隔符之前的简历项目
                items = self.page.query_selector_all(
                    "//div[@role='listitem'][contains(.//div/@class, 'recommend-resume-item') and following-sibling::div[@role='listitem'][.//p[contains(text(), '更多相关人才')]]]"
                )
                file_logger.log(f"检测到'更多相关人才'分隔符，获取到{len(items)}个有效简历项目")
            else:
                # 如果不存在分隔符，获取所有包含简历的项目
                items = self.page.query_selector_all(
                    "//div[@role='listitem'][.//div[contains(@class, 'recommend-resume-item')]]"
                )
                file_logger.log(f"未检测到分隔符，获取到{len(items)}个简历项目")


        except Exception as ea:
            file_logger.error(f"打招呼超时出问题: {ea}", exc_info=True)
            # 超时说明没有这个跳过进行下一个
            file_logger.log("打招呼超时，没有找到对那个数据，标记完成 : {}".format(zwno))
            try:
                PlaywrightUtils.wait_for_element(self.page, "//div[@role='listitem']")
                items = self.page.query_selector_all("//div[@role='listitem']")
            except Exception as e:
                file_logger.error("打招呼超时出问题 : {}".format(e))
                file_logger.log("打招呼超时，没有找到对那个数据，标记完成 : {}".format(zwno))
                sjudb.mark_complete(zwno)
                return
        
        if not items:
            file_logger.log("没有要处理的信息，标记完成")
            sjudb.mark_complete(zwno)
            print("没有要处理的信息")
            return
            # 有信息开始处理
        for item in items:
            if not self.is_start():
                self.continue_say_hallo = True
                file_logger.log("状态不是start，返回")
                return
            
            # 提取年龄信息并进行范围检查
            try:
                age_element = item.query_selector("//span[@class='age-label']")
                if age_element:
                    age_text = age_element.text_content().strip()
                    # 提取数字，去掉"岁"字
                    age_match = re.search(r'(\d+)', age_text)
                    if age_match:
                        current_age = int(age_match.group(1))
                        file_logger.log(f"提取到年龄: {current_age}岁")
                        
                        # 获取年龄范围配置
                        l_data = self.load_data()
                        if l_data:
                            ageStart = l_data.get("ageStart")
                            ageEnd = l_data.get("ageEnd")
                            limitAge = l_data.get("limitAge")
                            if not limitAge:
                                # 检查年龄是否在范围内
                                age_in_range = True
                                if ageStart and current_age < int(ageStart):
                                    age_in_range = False
                                    file_logger.log(f"年龄{current_age}小于最小年龄{ageStart}，跳过")
                                if ageEnd and current_age > int(ageEnd):
                                    age_in_range = False
                                    file_logger.log(f"年龄{current_age}大于最大年龄{ageEnd}，跳过")
                                
                                # 如果年龄不在范围内，跳过此项
                                if not age_in_range:
                                    continue
                                
                            file_logger.log(f"年龄{current_age}在范围内，继续处理")
                    else:
                        file_logger.log("无法从年龄文本中提取数字")
                else:
                    file_logger.log("未找到年龄标签元素")
            except Exception as e:
                file_logger.log(f"提取年龄时出错: {str(e)}")
            
            # 如果不可见，滚动到可见
            if not item.is_visible():
                file_logger.log("元素不可见，滚动到可见区域")
                item.scroll_into_view_if_needed()
                print("滚动元素")
                self.sleep(1)
            # 点击元素
            file_logger.log("等待2秒后点击打招呼按钮")
            self.sleep(2)
            if not self.has_nummmm():
                file_logger.log("已用完打招呼次数")
                self.dzh_success = True
                self.sleep(3)
                return
            # 这里拿整条信息
            
            if not self.is_debug:
                file_logger.log("点击打招呼按钮")


                # item.query_selector(
                # "//button[@class='large-screen-btn km-button km-control km-ripple-off km-button--primary km-button--filled is-fullwidth' and ./div[normalize-space(text())='打招呼']]").click()
                # # 判断如果出来了内容，点击一下，如果没有不用。
                try:
                    # 等待元素存在且可见
                    # dt = item.wait_for_selector(
                    #     "//div[@class='resume-button']//button[contains(@class,'km-button') and (.//div[normalize-space()='打招呼'] or .//following-sibling::span[normalize-space()='打招呼'])]",
                    #     state="visible",  # 确保可见
                    #     timeout=1000  # 根据需求调整超时时间
                    # )
                    dt = item.wait_for_selector(
                        "//div[@class='resume-button']//button[contains(@class,'km-button') and (.//div[normalize-space()='打招呼'] or .//span[normalize-space()='打招呼'])] | //div[@class='resume-button']//button[contains(@class,'km-button')]/following-sibling::button[.//span[normalize-space()='打招呼']]",
                        state="visible",
                        timeout=1000
                    )

                    # 验证元素是否可点击
                    dt.click()
                except PlaywrightTimeoutError:
                    file_logger.log("按钮未找到或不可点击，跳过")
                    continue

                self.sleep(1)

                # "//button[@class='km-button km-control km-ripple-off km-button--primary km-button--filled' and ./div[@class='km-ripple' and normalize-space(text())='发送']]"

                # "//div[@class='chat-set-greet__content-form-item chat-set-greet__content-textarea']//input[@name='greet']"
                try:
                    # 检查发送按钮是否存在
                    send_button_exists = self.page.query_selector(
                        "//button[@class='km-button km-control km-ripple-off km-button--primary km-button--filled' and ./div[@class='km-ripple' and normalize-space(text())='发送']]"
                    ) is not None
                    
                    if send_button_exists:
                        file_logger.log("检测到打招呼弹窗，准备输入内容并发送")
                        # 点击输入框
                        humanized_click(self.page, 
                            "(//div[@class='chat-set-greet__content-form-item chat-set-greet__content-textarea']//label/div)[1]")
                        self.sleep(1)
                        # 点击发送按钮
                        humanized_click(self.page, 
                            "//button[@class='km-button km-control km-ripple-off km-button--primary km-button--filled' and ./div[@class='km-ripple' and normalize-space(text())='发送']]")

                        humanized_click(self.page,"//button[@class='km-button km-control km-ripple-off km-button--primary km-button--filled' and ./div[@class='km-ripple' and normalize-space(text())='我知道了']]")
                        file_logger.log("已点击发送按钮")
                except Exception as e:
                    file_logger.log(f"处理打招呼弹窗时出错: {e}")


                try:
                    self.sleep(2)
                    if change_wechat:
                        # 再查找继续聊天按钮
                        dt.click()

                        humanized_click(self.page,
                                        "//a[@class='km-button km-control km-ripple-off km-button--light km-button--outlined' and .//span[normalize-space(text())='换微信']]")
                        try:
                            humanized_click(self.page,
                                            "//div[@class='km-popover__inner']//button[@class='km-button km-control km-ripple-off km-button--primary km-button--filled' and div[normalize-space(text())='确定']]")


                        except Exception as e:
                            humanized_click(self.page,
                                            "//div[@class='km-popover__inner']//button[@class='km-button km-control km-ripple-off km-button--primary km-button--filled' and div[normalize-space(text())='我知道了']]")
                            print("换微信出错", e)


                        humanized_click(self.page,"//i[@class='km-icon sati ignore-im-widget-click sati-times']")

                except Exception as e:
                    file_logger.log(f"处理打招呼弹窗时出错: {e}")
                    pass


            logger.save_log("sh", "打招呼完成")
            file_logger.log("打招呼完成，增加打招呼次数")
            # 添加次数
            sjudb.increment_cs(zwno)
            file_logger.log("获取职位最大打招呼次数")
            maxzw = shDb.get_data_cs("tableData",zwno)
            file_logger.log("获取当前职位已打招呼次数")
            curza = sjudb.get_currentzw_cs_sum(zwno)
            file_logger.log(f"当前职位已打招呼次数: {curza}，最大次数: {maxzw}")
            if curza >= maxzw:
                # 完成了
                file_logger.log("当前职位打招呼次数已达上限，标记完成")
                sjudb.mark_complete(zwno)
                self.sleep(3)
                return

            file_logger.log("检查是否有未读消息")
            # 检测是不是有新消息
            has_unread = self.check_unread_messages()
            if has_unread:
                self.continue_say_hallo = True
                file_logger.log("发现未读消息，返回")
                return
            self.sleep(time_sleep)


    def check_unread_messages(self):
        # element = self.page.query_selector("//a[@href='/app/im']//div[@class='app-im-unread']")
        # element = self.page.query_selector("//a[@href='/app/im']//div[@class='app-im-unread app-im-unread__max']")
        # return element
        # 定位互动菜单项中的未读消息元素
        try:
            unread_selector = '.app-nav__item:has-text("互动") .app-im-unread'
            unread_element = self.page.locator(unread_selector)
            
            # 添加超时处理
            is_visible = False
            try:
                is_visible = unread_element.first.is_visible(timeout=3000)
            except PlaywrightTimeoutError:
                return False
                
            if unread_element.count() > 0 and is_visible:
                unread_text = unread_element.first.inner_text().strip()
                print(f"有新消息: {unread_text}")
                return True
            else:
                print("没有新消息")
                return False
        except Exception as e:
            file_logger.error(f"检查未读消息时出错: {e}")
            return False



    def get_resume_by_page(self, chat_history, pi):

        current_id = get_session_id(self.page)

        """获取简历"""
        try:
            # 判断是不是有简历，有简历拿出来
            exists = self.page.query_selector(
                "//div[@class='resume-button get-phone']//div[@class='km-ripple' and text()='查看详情']") is not None

            exxx = False
            try:
                if chat_history[0]['type'] == "对方":
                    exxx = True
            except Exception as e:
                pass

            has_jl = check_jj(chat_history, "对方向您发送了在线简历")
            wechat_element = self.page.query_selector(
                '//div[@class="imc-wx-request__title--text--con" and contains(text(), "微信号：")]')
            if exists or exxx or has_jl or wechat_element :
                # 有简历，开始拿
                # 先判断是不是拿过
                hasUser = ruDb.get_list_by_id(current_id)
                
                is_complete = False

                if hasUser and len(hasUser) > 0:
                    user_record = hasUser[0]
                    # 数据库记录结构：(id, userId, create_time, name, ctno, datajson)
                    datajson_str = user_record[5]  # datajson\
                    is_complete = check_resume_completeness(datajson_str)


                if hasUser is None or len(hasUser) == 0 or not is_complete:
                    # 空，写入
                    humanized_click(self.page, "//div[@class='hover-resume-footer__button is-mr-8']")
                    self.sleep(4)
                    # self.page.wait_for_selector(".km-modal__wrapper new-shortcut-resume__modal", timeout=5000)
                    try:
                        humanized_click(self.page,
                                        "//div[@class='resume-basic-new__contacts--phone']//button[@class='km-button km-control km-ripple-off km-button--primary km-button--text is-ml-4']")
                    except  Exception as e:
                        file_logger.error("点击查看详情出错")
                    resume = extract_resume_data(self.page)
                    current_name = resume.get("personal_info",{}).get("name")

                    if hasUser and len(hasUser) > 0 and not is_complete:
                        # 存在记录但不完整，执行更新
                        ruDb.update_resume(current_name, current_id, resume)
                        file_logger.log(f"更新用户{current_id}的简历数据")
                    else:
                        # 不存在记录，执行插入
                        ruDb.insert_resume(current_name, current_id, resume)
                        file_logger.log(f"插入用户{current_id}的简历数据")

                    humanized_click(self.page, "//i[@class='km-icon sati sati-times-circle-s']")


        except Exception as e:
            print(e)
            file_logger.error("简历获取异常")
            file_logger.error(e)

        try:
            # 查找微信号元素
            wechat_element = self.page.query_selector(
                '//div[@class="imc-wx-request__title--text--con" and contains(text(), "微信号：")]')
            if wechat_element:
                # 提取微信号文本
                wechat_text = wechat_element.text_content()
                if wechat_text and "微信号：" in wechat_text:
                    wechat_number = wechat_text.replace("微信号：", "").strip()
                    file_logger.log(f"获取到微信号: {wechat_number}")
                    # 如果没有微信号，跳过当前用户
                    current_name = pi.get("name")
                    if wechat_number:
                        # 有微信号，更新用户数据
                        if ruDb.get_user_byDb(current_id):
                            ruDb.update_user_wechat_number(current_id, wechat_number)
                            # 没数据，拿简历
                            file_logger.log("保存微信号成功{}".format(wechat_number))
                        else:
                            resume = extract_resume_data2(current_name, wechat_number, pi)
                            ruDb.insert_resume(current_name, current_id, resume)
                            file_logger.log("保存微信号成功2{}".format(wechat_number))

                else:
                    file_logger.log("未找到微信号信息")
            else:
                file_logger.log("页面中没有微信号元素")
        except Exception as e:
            file_logger.error(f"获取微信号时出错: {e}")




    def process_unread_messages(self):

        is_hd = check_hd_page(self.page)
        if not is_hd:
            file_logger.log("不是聊天页面，返回")
            return

        PlaywrightUtils.wait_for_element(self.page,'div.km-checkbox:has(span:has-text("未读"))')

        checkbox = self.page.locator('div.km-checkbox:has(span:has-text("未读"))')

        # 双重状态检测机制
        is_checked = (
                "km-checkbox--checked" in checkbox.get_attribute("class")  # 通过类名检测
                or checkbox.locator("input[type=checkbox]").is_checked()  # 通过原生属性检测
        )

        if not is_checked:
            # 带防御性等待的点击
            checkbox.click(timeout=5000)
            print("已勾选未读过滤")
        else:
            checkbox.click(timeout=5000)
            self.page.wait_for_timeout(1000)
            checkbox.click(timeout=5000)

        elet = PlaywrightUtils.wait_for_element(self.page,"//div[@class='im-session-item km-list__item']")
        if elet is None:
            file_logger.log("没有找到会话列表")
            return
        # 判断是不是有信息
        items = self.page.query_selector_all("//div[@class='im-session-item km-list__item']")

        if not items:
            file_logger.log("没有找到需要沟通的内容!")
            return
        # 有信息开始处理
        for item in items:
            if not self.is_start():
                file_logger.log("暂停中，跳过本次循环")
                return

            scroll_view_and_click(item,self.page)

            # 先拿对面用户信息,判断是不是合格
            pi = self.get_people_info()
            file_logger.log("拿到了用户: {}".format(pi))
            if pi is None or len(pi) == 0:
                pi = self.get_people_info()
                if pi is None or len(pi) == 0:
                    file_logger.log("没有拿到用户信息: {}".format(pi))
                    continue

            logger.save_log("gt", "准备和 {} 进行沟通".format(pi.get("name")))
            try:
                chat_history = parse_chat_messages(self.page)
                if check_jj(chat_history):
                    logger.save_log("gt", "检测到拒绝，跳过处理")
                    continue

                file_logger.log("解析到的消息: {}".format(chat_history))
            except Exception as e:
                file_logger.error("解析消息出错")
                continue


            if self.is_chat_closed():
                logger.save_log("gt", "聊天已关闭，跳过处理")
                file_logger.log("聊天已关闭，跳过处理")
                print("聊天关闭了")
                continue


            # 判断年龄限制
            if not self.gt_all_data:
                file_logger.log("没有保存沟通....")
                return


            # 限制年龄
            if not self.gt_all_data.get("limitAge"):
                if self.gt_all_data.get("limitAgeText", {}).get("start"):
                    if int(pi.get("age")) < int(self.gt_all_data.get("limitAgeText", {}).get("start")):
                        logger.save_log("gt", "{} 年龄为 {}, 不符合要求".format(pi.get("name"), pi.get("age")))
                        if self.gt_all_data.get("pass", {}).get("age") and len(self.gt_all_data.get("pass", {}).get("age")) > 0:
                            self.send_message_by_keyboard(self.gt_all_data.get("pass", {}).get("age"))
                            # 点击不合适
                            logger.save_log("gt", "和 {} 沟通 年龄不合适发送了 {}".format(pi.get("name"),
                                                                                          self.gt_all_data.get("pass",
                                                                                                               {}).get(
                                                                                              "age")))
                        humanized_click2(self.page,
                                         "//a[@class='km-button km-control km-ripple-off km-button--light km-button--outlined' and .//div[@class='negative-feedback-trigger']]")

                        continue
                if self.gt_all_data.get("limitAgeText", {}).get("end"):
                    if int(pi.get("age")) > int(self.gt_all_data.get("limitAgeText", {}).get("end")):
                        logger.save_log("gt", "{} 年龄为 {}, 不符合要求".format(pi.get("name"), pi.get("age")))
                        if self.gt_all_data.get("pass", {}).get("age") and len(self.gt_all_data.get("pass", {}).get("age")) > 0:
                            self.send_message_by_keyboard(self.gt_all_data.get("pass", {}).get("age"))
                            logger.save_log("gt", "和 {} 沟通 年龄不合适发送了 {}".format(pi.get("name"),
                                                                                          self.gt_all_data.get("pass",
                                                                                                               {}).get(
                                                                                              "age")))
                        humanized_click2(self.page,
                                         "//a[@class='km-button km-control km-ripple-off km-button--light km-button--outlined' and .//div[@class='negative-feedback-trigger']]")

                        continue
            # 性别
            if not self.gt_all_data.get("limitSex"):
                if pi.get("sex") == '':
                    logger.save_log("gt", "{} 性别位置 不符合要求".format(pi.get("name")))
                    humanized_click2(self.page,
                                     "//a[@class='km-button km-control km-ripple-off km-button--light km-button--outlined' and .//div[@class='negative-feedback-trigger']]")

                    continue
                if self.gt_all_data.get("limitSexText", None):
                    if pi.get("sex") != self.gt_all_data.get("limitSexText"):
                        logger.save_log("gt", "{} 性别为 {}, 不符合要求".format(pi.get("name"), pi.get("sex")))
                        if self.gt_all_data.get("pass", {}).get("sex") and len(self.gt_all_data.get("pass", {}).get("sex")) > 0:
                            self.send_message_by_keyboard(self.gt_all_data.get("pass", {}).get("sex"))

                            logger.save_log("gt", "和 {} 沟通 性别不合适发送了 {}".format(pi.get("name"),
                                                                                          self.gt_all_data.get("pass",
                                                                                                               {}).get(
                                                                                              "sex")))
                        humanized_click2(self.page,
                                         "//a[@class='km-button km-control km-ripple-off km-button--light km-button--outlined' and .//div[@class='negative-feedback-trigger']]")

                        continue

            # 学历
            if not self.gt_all_data.get("limitXl"):
                if self.gt_all_data.get("limitXlList", None):
                    if pi.get("xl") not in self.gt_all_data.get("limitXlList"):
                        logger.save_log("gt", "{} 学历为 {}, 不符合要求".format(pi.get("name"), pi.get("xl")))
                        if self.gt_all_data.get("pass", {}).get("xl")  and len(self.gt_all_data.get("pass", {}).get("xl")) > 0:
                            self.send_message_by_keyboard(self.gt_all_data.get("pass", {}).get("xl"))
                            logger.save_log("gt", "和 {} 沟通 学历不合适发送了 {}".format(pi.get("name"),
                                                                                          self.gt_all_data.get("pass",
                                                                                                               {}).get(
                                                                                              "xl")))
                        humanized_click2(self.page,
                                         "//a[@class='km-button km-control km-ripple-off km-button--light km-button--outlined' and .//div[@class='negative-feedback-trigger']]")

                        continue

            self.get_resume_by_page(chat_history, pi)

            tl = get_last_who(chat_history)

            if tl['type'] == '我方':
                file_logger.log("最后说话是我方说的，不回复...")
                continue

            # 对方说的话
            t_content = tl['text']

            hasFiled = False
            exCia = ''
            if self.gt_all_data.get("filterPeople"):
                # 过滤人群
                if self.gt_all_data.get("filterPeopleText"):
                    for exCi in self.gt_all_data.get("filterPeopleText").split(" "):
                        if exCi in t_content:
                            hasFiled = True
                            exCia = exCi
                            break

            if hasFiled:
                logger.save_log("gt", "{} 说了 {} , 不符合要求".format(pi.get("name"), exCia))
                if self.gt_all_data.get("pass", {}).get("filter") and len(
                        self.gt_all_data.get("pass", {}).get("filter")) > 0:
                    self.send_message_by_keyboard(self.gt_all_data.get("pass", {}).get("filter"))
                    logger.save_log("gt", "和 {} 沟通 学历不合适发送了 {}".format(pi.get("name"),
                                                                                  self.gt_all_data.get(
                                                                                      "pass",
                                                                                      {}).get(
                                                                                      "filter")))
                humanized_click2(self.page,
                                 "//a[@class='km-button km-control km-ripple-off km-button--light km-button--outlined' and .//div[@class='negative-feedback-trigger']]")

                continue

            otherNum = get_you_num(chat_history)
            meNum = get_me_num(chat_history)
            if otherNum <= 2 and meNum == 0:
                file_logger.log("对方发送了第一条消息，准备和他沟通")
                # 回复随机第一句
                jz_list = []
                if not is_empty(self.gt_all_data.get("random1")):
                    jz_list.append(self.gt_all_data.get("random1"))
                if not is_empty(self.gt_all_data.get("random2")):
                    jz_list.append(self.gt_all_data.get("random2"))
                if not is_empty(self.gt_all_data.get("random3")):
                    jz_list.append(self.gt_all_data.get("random3"))
                res_con = random.choice(jz_list)

                if res_con:

                    self.send_message_by_keyboard(res_con)
                    logger.save_log("gt", "和 {} 沟通 发送了 {}".format(pi.get("name"), res_con))
                    file_logger.log("gt", "和 {} 沟通 发送了 {}".format(pi.get("name"), res_con))

                    if not self.is_debug:
                        if self.gt_all_data.get("changeWechat"):
                            humanized_click(self.page,
                                            "//a[@class='km-button km-control km-ripple-off km-button--light km-button--outlined' and .//span[normalize-space(text())='换微信']]")
                            try:
                                # humanized_click(self.page,
                                                # "//button[@class='km-button km-control km-ripple-off km-button--primary km-button--filled']")
                                humanized_click(self.page,
                                                "//div[@class='km-popover__inner']//button[@class='km-button km-control km-ripple-off km-button--primary km-button--filled' and div[normalize-space(text())='确定']]")
                                logger.save_log("gt", "和 {} 申请换微信".format(pi.get("name")))

                            except Exception as e:
                                print("换微信出错", e)

                continue

            is_w = get_you_wechat(chat_history)
            file_logger.log("判断要过微信: {}".format(is_w))
            res_con = self.response_func(t_content,is_w)
            file_logger.log("回复内容: {}".format(res_con))
            if res_con:
                self.send_message_by_keyboard(res_con)
                logger.save_log("gt", "和 {} 沟通 发送了 {}".format(pi.get("name"), res_con))
                file_logger.log("回复内容: {}".format(res_con))
                if not self.is_debug:

                    if self.gt_all_data.get("changeWechat"):
                        if not check_jj(chat_history, "我想与您交换微信，方便沟通"):
                            humanized_click(self.page,
                                            "//a[@class='km-button km-control km-ripple-off km-button--light km-button--outlined' and .//span[normalize-space(text())='换微信']]")
                            try:
                                humanized_click(self.page,
                                                "//div[@class='km-popover__inner']//button[@class='km-button km-control km-ripple-off km-button--primary km-button--filled' and div[normalize-space(text())='确定']]")

                                logger.save_log("gt", "和 {} 申请换微信".format(pi.get("name")))

                            except Exception as e:
                                humanized_click(self.page,
                                                "//div[@class='km-popover__inner']//button[@class='km-button km-control km-ripple-off km-button--primary km-button--filled' and div[normalize-space(text())='我知道了']]")
                                print("换微信出错", e)

            self.page.wait_for_timeout(2000)


    def is_chat_closed(self):
        """判断是否存在'对方关闭了聊天'的提示"""
        # 使用类名定位元素
        element = self.page.query_selector(".im-exclusive-company__text")

        # 同时检查元素存在且文本匹配
        if element and element.inner_text().strip() == "对方关闭了聊天，无法继续沟通":
            return True
        return False

    def send_message_by_keyboard(self, content):



        textarea = self.page.wait_for_selector(
            'xpath=//textarea[contains(@class, "km-input__original")]',
            timeout=15000
        )

        # 先聚焦元素（触发可能的验证逻辑）
        textarea.focus()
        # 直接填充内容（立即生效）
        self.page.keyboard.type(content)
        self.sleep(3)
        # 按回车键
        if not self.is_debug:
            self.page.keyboard.press('Enter')



    def launch_browser(self, p):
        """启动浏览器并打开页面"""
        try:
            file_logger.log("开始启动浏览器")
            
            # 确定用户数据目录路径
            if getattr(sys, 'frozen', False):
                base_dir = os.path.dirname(sys.executable)
                user_data_dir = os.path.join(base_dir, "browser_data")
            else:
                user_data_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "browser_data")
            
            os.makedirs(user_data_dir, exist_ok=True)
            file_logger.log(f"使用用户数据目录: {user_data_dir}")
        
            try:
                executable_path = True
                if executable_path:
                    file_logger.log(f"使用系统Chrome: {executable_path}")
                    
                    
                    browser_args = [
                        '--start-maximized',
                        '--disable-blink-features=AutomationControlled',
                        '--exclude-switches=enable-automation',
                        '--disable-notifications',  # 禁用通知
                        '--disable-extensions',
                        '--no-first-run',  # 跳过首次运行
                          '--disable-features=CommandLineFlagSecurityWarnings',  # 必须保留
                        # === 隐藏所有浏览器警告 ===
                        '--disable-infobars',
                        '--no-default-browser-check',
                        '--disable-component-update',
                        '--disable-background-networking',
                        '--no-default-browser-check',  # 不检查默认浏览器
                        '--disable-default-apps',  # 禁用默认应用提示
                        '--disable-translate',  # 禁用翻译提示
                        '--disable-features=TranslateUI',  # 禁用翻译UI
                        '--disable-component-update',  # 禁用组件更新提示
                        '--disable-background-networking',  # 禁用后台网络
                        '--disable-sync',  # 禁用同步提示
                        '--hide-crash-restore-bubble',  # 隐藏崩溃恢复气泡
                        '--disable-features=VizDisplayCompositor',  # 专门针对安全警告的参数
                        '--lang=zh-CN',
                        '--window-size=1366,768',
                        # 沙盒相关（为了稳定性）
                        '--no-sandbox',
                        "--disable-features=Infobars",
                        '--disable-features=CommandLineFlagSecurityWarnings',
                        # 减少日志和报告
                        '--disable-logging',
                        '--disable-crash-reporter',
                        '--silent',
                         # === 新增内存优化参数 ===
                        '--memory-pressure-off',  # 关闭内存压力检测
                        '--max_old_space_size=4096',  # 限制V8引擎内存使用
                        '--disable-background-timer-throttling',  # 禁用后台定时器节流
                        '--disable-renderer-backgrounding',  # 禁用渲染器后台化
                        '--disable-backgrounding-occluded-windows',  # 禁用被遮挡窗口的后台化
                        '--disable-features=TranslateUI,BlinkGenPropertyTrees',
                        '--aggressive-cache-discard',  # 积极丢弃缓存
                        '--memory-pressure-off',  # 关闭内存压力
                        '--max-memory-usage=2048',  # 限制最大内
                        # 移除 --test-type 等明显的测试标识
                    ]
             
                                        
                    # 使用launch_persistent_context，但不手动操作页面
                    self.context = p.chromium.launch_persistent_context(
                        user_data_dir,
                        headless=False,
                        args=browser_args,
                        viewport={"width": 1366, "height": 768},
                        locale="zh-CN",
                        timezone_id="Asia/Shanghai",
                        chromium_sandbox=False, 
                        user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
                        ignore_default_args=['--enable-automation', '--enable-blink-features=AutomationControlled']
                        
                    )
                    
                else:
                    file_logger.error("未找到系统Chrome，请安装Chrome浏览器后再运行程序")
                    if hasattr(self, 'status_signal'):
                        self.status_signal.emit("错误：未找到Chrome浏览器，请安装后再运行")
                    return False
            except Exception as chrome_error:
                file_logger.log(f"使用系统Chrome失败: {chrome_error}，使用Playwright内置浏览器")
                self.browser = p.chromium.launch(headless=False, args=['--start-maximized'])
            
    
            # 添加反检测脚本
            self.context.add_init_script("""
            // 完全移除webdriver痕迹
            Object.defineProperty(navigator, 'webdriver', {
                get: () => undefined,
                configurable: true
            });
            
            // 删除所有自动化相关属性
            delete navigator.__proto__.webdriver;
            delete window.cdc_adoQpoasnfa76pfcZLmcfl_Array;
            delete window.cdc_adoQpoasnfa76pfcZLmcfl_Promise;
            delete window.cdc_adoQpoasnfa76pfcZLmcfl_Symbol;
            delete window.cdc_adoQpoasnfa76pfcZLmcfl_Object;
            delete window.cdc_adoQpoasnfa76pfcZLmcfl_JSON;
            delete window.cdc_adoQpoasnfa76pfcZLmcfl_Function;
            
            // 覆盖chrome对象
            Object.defineProperty(window.navigator, 'chrome', {
                get: () => ({
                    app: { isInstalled: false },
                    webstore: {
                        onInstallStageChanged: {},
                        onDownloadProgress: {}
                    },
                    runtime: {
                        PlatformOs: {
                            MAC: 'mac',
                            WIN: 'win',
                            ANDROID: 'android',
                            CROS: 'cros',
                            LINUX: 'linux',
                            OPENBSD: 'openbsd'
                        },
                        PlatformArch: {
                            ARM: 'arm',
                            X86_32: 'x86-32',
                            X86_64: 'x86-64'
                        }
                    }
                }),
                configurable: true
            });
            
            // 禁用所有弹窗和提示
            window.alert = () => {};
            window.confirm = () => true;
            window.prompt = () => null;
            
            // 禁用权限请求
            Object.defineProperty(navigator, 'permissions', {
                get: () => ({
                    query: () => Promise.resolve({ state: 'denied' })
                }),
                configurable: true
            });
            
            // 修改语言设置
            Object.defineProperty(navigator, 'languages', {
                get: () => ['zh-CN', 'zh', 'en-US', 'en'],
                configurable: true
            });
            
            // 修改硬件并发数
            Object.defineProperty(navigator, 'hardwareConcurrency', {
                get: () => 8,
                configurable: true
            });
            
            // 修改设备内存
            Object.defineProperty(navigator, 'deviceMemory', {
                get: () => 8,
                configurable: true
            });
            """)
            
            # 简化页面获取：直接使用第一个可用页面
            pages = self.context.pages
            if pages:
                file_logger.log(f"发现 {len(pages)} 个现有页面，准备关闭旧页面")
                # 创建新页面
                self.page = self.context.new_page()
                file_logger.log("已创建新标签页")
                
                # 关闭所有旧页面
                for old_page in pages:
                    try:
                        old_page.close()
                        file_logger.log("已关闭一个旧标签页")
                    except Exception as e:
                        file_logger.log(f"关闭旧页面时出错: {e}")
            else:
                # 如果没有页面，创建新页面
                self.page = self.context.new_page()
                file_logger.log("创建了第一个标签页")

            # 增强弹窗处理
            self.page.on("dialog", lambda dialog: dialog.accept())
            self.page.on("popup", lambda popup: popup.close())
            self.page.on("crash", lambda: file_logger.log("页面崩溃，准备重启"))
            self.setup_auto_popup_closer()

            # 设置超时时间
            self.page.set_default_timeout(5000)
            
            # 导航到登录页面
            file_logger.log("导航到登录页面")
            self.page.goto('https://rd6.zhaopin.com/app/index')


            


            # 等待页面加载完成
            self.page.wait_for_load_state('networkidle')
            file_logger.log("页面加载完成")

            return True
        except Exception as e:
            file_logger.error(f"启动浏览器失败: {e}", exc_info=False)
            return False


    
    def setup_auto_popup_closer(self):
        """设置自动弹窗关闭器"""
        try:
            file_logger.log("设置自动弹窗关闭器")
            
            # 注入自动关闭弹窗的JavaScript代码
            self.page.add_init_script("""
            (function() {
                console.log('初始化自动弹窗关闭器');
                
                // 弹窗关闭函数
                function autoClosePopup(element) {
                    try {
                        console.log('检测到弹窗，准备自动关闭:', element);
                        
                        // 方法1: 查找并点击关闭按钮
                        const closeSelectors = [
                            '.km-modal__close-btn',
                            '.km-modal__close-icon', 
                            '.close',
                            '[class*="close"]',
                            '[class*="Close"]',
                            'button[aria-label="Close"]',
                            'button[title="关闭"]'
                        ];
                        
                        for (let selector of closeSelectors) {
                            const closeBtn = element.querySelector(selector);
                            if (closeBtn && closeBtn.offsetParent !== null) { // 检查是否可见
                                console.log('找到关闭按钮，点击:', selector);
                                closeBtn.click();
                                return true;
                            }
                        }
                        
                        // 方法2: 如果是模态框，尝试点击遮罩层关闭
                        if (element.classList.contains('km-modal__wrapper')) {
                            console.log('点击模态框遮罩层关闭');
                            element.click();
                            return true;
                        }
                        
                        // 方法3: 发送ESC键
                        console.log('尝试ESC键关闭');
                        document.dispatchEvent(new KeyboardEvent('keydown', {
                            key: 'Escape',
                            keyCode: 27,
                            which: 27,
                            bubbles: true,
                            cancelable: true
                        }));
                        
                        // 方法4: 延迟后强制移除（最后手段）
                        setTimeout(() => {
                            if (element.parentNode && element.offsetParent !== null) {
                                console.log('强制移除弹窗元素');
                                element.style.display = 'none';
                                // 或者完全移除: element.parentNode.removeChild(element);
                            }
                        }, 2000);
                        
                        return true;
                        
                    } catch (error) {
                        console.error('自动关闭弹窗时出错:', error);
                        return false;
                    }
                }
                
                // 监控DOM变化，检测新增的弹窗
                const observer = new MutationObserver((mutations) => {
                    mutations.forEach((mutation) => {
                        if (mutation.type === 'childList') {
                            mutation.addedNodes.forEach((node) => {
                                if (node.nodeType === 1) { // 元素节点
                                    // 检查是否为弹窗
                                    if (node.classList && (
                                        node.classList.contains('km-modal__wrapper') ||
                                        node.classList.contains('image-popup-modal') ||
                                        node.classList.contains('km-modal') ||
                                        node.querySelector('.km-modal__wrapper') ||
                                        node.querySelector('.image-popup-modal')
                                    )) {
                                        console.log('DOM变化检测到弹窗');
                                        // 延迟一点时间让弹窗完全显示后再关闭
                                        setTimeout(() => {
                                            autoClosePopup(node);
                                        }, 300);
                                    }
                                    
                                    // 检查子元素中是否有弹窗
                                    const childPopups = node.querySelectorAll && node.querySelectorAll('.km-modal__wrapper, .image-popup-modal, .km-modal');
                                    if (childPopups && childPopups.length > 0) {
                                        console.log('在子元素中发现弹窗');
                                        childPopups.forEach(popup => {
                                            setTimeout(() => {
                                                autoClosePopup(popup);
                                            }, 300);
                                        });
                                    }
                                }
                            });
                        }
                        
                        // 监控属性变化（比如style变化显示弹窗）
                        if (mutation.type === 'attributes' && 
                            mutation.attributeName === 'style' && 
                            mutation.target.classList &&
                            (mutation.target.classList.contains('km-modal__wrapper') ||
                             mutation.target.classList.contains('image-popup-modal'))) {
                            
                            const element = mutation.target;
                            if (element.offsetParent !== null) { // 检查是否变为可见
                                console.log('属性变化检测到弹窗显示');
                                setTimeout(() => {
                                    autoClosePopup(element);
                                }, 300);
                            }
                        }
                    });
                });
                
                // 开始监控整个文档
                observer.observe(document.body, {
                    childList: true,
                    subtree: true,
                    attributes: true,
                    attributeFilter: ['style', 'class']
                });
                
                // 立即检查页面上已存在的弹窗
                function checkExistingPopups() {
                    const existingPopups = document.querySelectorAll('.km-modal__wrapper, .image-popup-modal, .km-modal--open');
                    if (existingPopups.length > 0) {
                        console.log('发现现有弹窗:', existingPopups.length, '个');
                        existingPopups.forEach(popup => {
                            if (popup.offsetParent !== null) { // 只处理可见的
                                setTimeout(() => {
                                    autoClosePopup(popup);
                                }, 500);
                            }
                        });
                    }
                }
                
                // 页面加载完成后检查
                if (document.readyState === 'loading') {
                    document.addEventListener('DOMContentLoaded', checkExistingPopups);
                } else {
                    checkExistingPopups();
                }
                
                // 定期检查（备用方案）
                setInterval(checkExistingPopups, 5000); // 每5秒检查一次
                
                console.log('自动弹窗关闭器设置完成');
            })();
            """)
            
            file_logger.log("自动弹窗关闭器设置完成")
            
        except Exception as e:
            file_logger.error(f"设置自动弹窗关闭器失败: {e}")
    
    def setup_page(self):
        """初始化页面设置"""
        try:
            file_logger.log("开始设置页面")
            # self.page.goto('https://rd6.zhaopin.com/app/index')

            # 设置全屏
            try:
                self.page.evaluate("""() => {
                    if (document.documentElement.requestFullscreen) {
                        document.documentElement.requestFullscreen();
                    }
                }""")
            except Exception as e:
                file_logger.log(f"设置全屏时出错: {e}")
                
            # 添加反检测脚本
            try:
                self.page.add_init_script("""
                // 完全移除webdriver痕迹
                Object.defineProperty(navigator, 'webdriver', {
                    get: () => undefined,
                    configurable: true
                });
                
                // 删除自动化相关属性
                delete navigator.__proto__.webdriver;
                delete window.cdc_adoQpoasnfa76pfcZLmcfl_Array;
                delete window.cdc_adoQpoasnfa76pfcZLmcfl_Promise;
                delete window.cdc_adoQpoasnfa76pfcZLmcfl_Symbol;
                
                // 覆盖chrome对象
                window.navigator.chrome = {
                    app: { isInstalled: false },
                    webstore: {
                        onInstallStageChanged: {},
                        onDownloadProgress: {}
                    },
                    runtime: {
                        PlatformOs: {
                            MAC: 'mac',
                            WIN: 'win',
                            ANDROID: 'android',
                            CROS: 'cros',
                            LINUX: 'linux',
                            OPENBSD: 'openbsd'
                        },
                        PlatformArch: {
                            ARM: 'arm',
                            X86_32: 'x86-32',
                            X86_64: 'x86-64'
                        }
                    }
                };
                
                // 覆盖权限API
                const originalQuery = window.navigator.permissions.query;
                window.navigator.permissions.query = (parameters) => (
                    parameters.name === 'notifications' ?
                        Promise.resolve({ state: Notification.permission }) :
                        originalQuery(parameters)
                );
                
                // 伪造插件信息
                Object.defineProperty(navigator, 'plugins', {
                    get: () => {
                        return [
                            {
                                0: {
                                    type: "application/x-google-chrome-pdf",
                                    suffixes: "pdf",
                                    description: "Portable Document Format",
                                    enabledPlugin: Plugin
                                },
                                description: "Portable Document Format",
                                filename: "internal-pdf-viewer",
                                length: 1,
                                name: "Chrome PDF Plugin"
                            }
                        ];
                    },
                    configurable: true
                });
                """)
            except Exception as e:
                file_logger.log(f"添加反检测脚本时出错: {e}")
                
            self.status_signal.emit("页面初始化完成")
            file_logger.log("页面设置完成")
        except Exception as e:
            file_logger.error(f"设置页面时出错: {e}", exc_info=True)
            raise


    def handle_greeting(self, zwno):
        # 先判断是第几次
        zwno=int(zwno)
        """处理打招呼任务"""
        print("=-----执行")
        ck = ClickUtils.click_and_wait_url_change(self.page,
                                             "//a[@href='/app/recommend' and span[text()='推荐人才']]",['app/recommend'])
        if not ck:
            file_logger.log("点击页面失败")
            return False
        self.sleep(1)
        cur_page = check_gt_page(self.page)
        if not cur_page:
            file_logger.log("页面不对，出去重新进")
            return False
        # 先选择职位
        humanized_click(self.page, "//a[@zp-stat-id='talent_more_jobs']")
        # 选择第几个职位,获取职位列表,这里是职位选择。
        self.sleep(1)

        # todo 前边加上数据库获取该第几个了
        PlaywrightUtils.wait_for_element(self.page, "//div[@class='job-side-selector__inner']",retries=5)
        items = self.page.query_selector_all("//div[@class='job-side-selector__inner']")

        if (len(items)) < zwno:
            # 滚动到最后一个元素
            last_item = items[-1]

            for _ in range( int((zwno/20)+1)):
                last_item.scroll_into_view_if_needed()
                self.sleep(2)  # 等待可能的懒加载
            # 重新获取所有元素
            items = self.page.query_selector_all("//div[@class='job-side-selector__inner']")
            if (len(items)) < zwno:
                # 滚动到最后一个元素
                last_item = items[-1]
                last_item.scroll_into_view_if_needed()
                self.sleep(2)  # 等待可能的懒加载

                # 重新获取所有元素
                items = self.page.query_selector_all("//div[@class='job-side-selector__inner']")
                if (len(items)) < zwno:
                    self.page.reload()
                    self.sleep(1)
                    humanized_click(self.page, "//a[@href='/app/im']")
                    raise Exception("没有这个职位")

        try:
            a = items[zwno - 1]
            a.click()
        except:
            self.page.reload()
            self.sleep(1)
            humanized_click(self.page, "//a[@href='/app/im']")
            raise Exception("没有这个职位")

        # 点击筛选
        humanized_click(self.page, "//i[@class='km-icon sk is-mr-4 sk-filter']")

        humanized_click(self.page, "//div[@class='km-ripple']//span[@class='footer-clear__text']")


        l_data = self.load_data()
        if not l_data:
            file_logger.log("没有打招呼数据....")
            self.sleep(1)
        file_logger.log("dddd: {}".format(l_data))
        limitAge = l_data.get("limitAge")
        ageStart = l_data.get("ageStart")
        ageEnd = l_data.get("ageEnd")
        file_logger.log("limitAge: {}, ageStart: {}, endAge:{}".format(limitAge, ageStart, ageEnd))
        if not limitAge and (ageStart or ageEnd):
            # 这里先点击编辑年龄
            humanized_click2(self.page,"//div[@class='tr-talent-filter-item filter-item-age']//div[contains(@class, 'recommend-checkbox-group__item') and normalize-space(text())='自定义']")
            # 开始输入年龄
            if ageStart:
                humanized_click2(self.page,
                                "(//div[@class='recommend-checkbox-group__selector']//div[@class='km-input__icon'])[1]")
                humanized_click(self.page, "//a[@class='km-option' and div[text()='{}岁']]".format(ageStart))
            if ageEnd:
                humanized_click2(self.page,
                                "(//div[@class='recommend-checkbox-group__selector']//div[@class='km-input__icon'])[2]")
                humanized_click(self.page, "(//a[@class='km-option' and div[text()='{}岁']])[2]".format(ageEnd))

        # 活跃日期
        activityLevel = l_data.get("activityLevel")
        if activityLevel:
            for a in activityLevel:
                humanized_click2(self.page, f"//div[@class='km-radio__label' and normalize-space()='{a}']")

        # 学历
        limitXl = l_data.get("limitXl")
        checkList = l_data.get("checkList")
        if not limitXl and checkList:
            for a in checkList:
                humanized_click2(self.page,
                                 f"//div[contains(@class, 'recommend-checkbox-group__item') and span[normalize-space()='{a}']]")

        # 意向
        limityx = l_data.get("limityx")
        checkList2 = l_data.get("checkList2")
        if not limityx and checkList2:
            for a in checkList2:
                humanized_click2(self.page,
                                f"//div[contains(@class, 'recommend-checkbox-group__item') and span[normalize-space()='{a}']]")

        # 性别
        limitSex = l_data.get("limitSex")
        genderList = l_data.get("genderList")
        if not limitSex and genderList:
            for a in genderList:
                humanized_click2(self.page, f"//label[@class='km-radio km-radio--primary' and div[text()='{a}']]")

        filterColleagues = l_data.get("filterColleagues")
        if filterColleagues:
            humanized_click2(self.page,
                            f"//div[contains(@class, 'recommend-checkbox-group__item') and span[normalize-space()='过滤同事已聊']]")

        # 人才范围

        humanized_click(self.page,
                        "//button[@class='km-button km-control km-ripple-off km-button--primary km-button--filled' and @zp-stat-id='rsmlist-confirm']")

        return True
    def get_name(self):
        root = self.page.query_selector('.im-resume-detail')
        return safe_query(root, '.new-resume-basic__name')

    def get_people_info(self):
        xpath_selector = '//div[@class="new-resume-basic__header-con"]'
        PlaywrightUtils.wait_for_element(self.page, xpath_selector)

        # 定位根元素
        header = self.page.query_selector(xpath_selector)
        if not header:
            file_logger.log("未找到简历头部容器")
            return {}

        # 初始化结果字典
        result = {
            "name": "",
            "sex": "",
            "active": "",
            "age": "",
            "xl": "",
            "status": ""
        }

        # 提取姓名
        name_element = header.query_selector('xpath=.//span[@class="new-resume-basic__name"]')
        if name_element:
            result["name"] = name_element.inner_text().strip()

        img_src = self.page.locator("div.km-image.km-image--loaded.new-resume-basic__portrait-icon > img").get_attribute(
            "src")
        if img_src:
            result["sex"] = "女" if "woman" in img_src else "男"
        # 提取活跃状态
        active_element = header.query_selector('xpath=.//span[contains(@class, "new-resume-basic__state")]')
        if active_element:
            result["active"] = active_element.inner_text().strip()

        # 提取基本信息（年龄、学历、状态）
        infos_element = header.query_selector('xpath=.//div[@class="new-resume-basic__infos"]')
        if infos_element:
            # 等待 infos_element 内容加载

            # 获取所有子节点的文本（包括直接文本和嵌套标签中的文本）
            text_nodes = infos_element.query_selector_all('xpath=.//text()[normalize-space()]')
            text_parts = [node.inner_text().strip() for node in text_nodes if node.inner_text().strip()]

            print("提取的 text_parts:", text_parts)  # 调试用

            # 根据顺序提取字段
            if len(text_parts) >= 3:
                result["age"] = text_parts[0].replace("岁", "")
                result["xl"] = text_parts[1]
                result["status"] = text_parts[2]
            else:
                # 如果 text_parts 为空或不足，尝试获取完整文本并拆分
                infos_text = infos_element.inner_text().strip()
                print("infos_text 完整文本:", infos_text)
                if infos_text:
                    # 假设格式为 "28岁大专离职-正在找工作"，用正则拆分
                    pattern = r'(\d+)岁?([高中|中专|大专|本科|硕士|博士]+)?(.+)?'
                    match = re.match(pattern, infos_text)
                    if match:
                        result["age"] = match.group(1)
                        result["xl"] = match.group(2) if match.group(2) else ""
                        result["status"] = match.group(3).strip() if match.group(3) else ""

        result['age'] = result['age'].replace("岁", "")
        return result

    def get_item_chat(self):

        self.page.wait_for_selector('xpath=//div[@class="km-list__item im-message"]')
        try:
            chat_history = parse_chat_messages(self.page)
        except:
            return
        if len(chat_history) == 0:
            return
        dt = chat_history[-1]
        if dt['type'] != "对方":
            return
        if dt['text']:
            if self.response_func:
                # todo, 这里加一个拒绝的话，不发消息，不发微信
                res = self.response_func(dt['text'])
                if res:
                    textarea = self.page.wait_for_selector(
                        'xpath=//textarea[contains(@class, "km-input__original")]',
                        timeout=15000
                    )

                    # 先聚焦元素（触发可能的验证逻辑）
                    textarea.focus()
                    # 直接填充内容（立即生效）
                    textarea.fill(res)
                    # todo
                    if gtDb.get_json_value("changeWechat"):
                        humanized_click(self.page,
                                        "//a[@class='km-button km-control km-ripple-off km-button--light km-button--outlined' and .//span[normalize-space(text())='换微信']]")

    def handle_monitoring(self):
        """监控聊天或后续任务"""
        print("监控聊天中...")
        if self.check_new_message():
            self.status = "chatting"
            self.status_signal.emit("检测到新消息，进入聊天模式")
        self.sleep(1)  # 监控间隔

    def check_new_message(self):
        """检查是否有新消息"""
        # 示例逻辑，需根据实际页面调整
        # return self.page.query_selector('.new-message') is not None
        return False  # 占位，模拟无新消息

    def load_data(self):
        """从数据库加载数据"""
        with sqlite3.connect(self.db_path) as conn:
            cursor = conn.cursor()
            cursor.execute("SELECT datajson FROM sh WHERE userId = 1")
            result = cursor.fetchone()
            return json.loads(result[0]) if result else None

    def load_re_data(self):
        with sqlite3.connect(self.db_path) as conn:
            cursor = conn.cursor()
            cursor.execute("SELECT datajson FROM fl WHERE userId = 1")
            result = cursor.fetchone()
            return json.loads(result[0]) if result else None

    def get_data(self, key):
        """从数据库加载数据"""
        with sqlite3.connect(self.db_path) as conn:
            cursor = conn.cursor()
            cursor.execute("SELECT datajson FROM sh WHERE userId = 1")
            result = cursor.fetchone()
            if not result:
                return None

            return json.loads(result[0]).get(key)

    def close_browser(self):
        """关闭浏览器"""
        if self.browser:
            self.browser.close()
            self.browser = None
            self.page = None
            self.status_signal.emit("浏览器已关闭")

    # def stop(self):
    #     """停止线程"""
    #     print("停止了。。。。。。")
    #     self.running = False  # 设置标志位为 False
    #     self.status = "idle"
    #     print("结束了...")


